package br.com.ac.fatureweb.integration.utilitary;

public final class Mod11 {

	/**
	 * Peso Inicial para o sistema de peso do MOD-11.
	 */
	public static final byte BASE_INITIAL = 2;

	/**
	 * Peso Final para o sistema de peso do MOD-11.
	 */
	public static final byte BASE_FINAL = 9;

	/**
	 * Valor inicial para o digito verificador do MOD-11.
	 */
	public static final byte DV_INITIAL = 0;

	/**
	 * Valor final para o digito verificador do MOD-11.
	 */
	public static final byte DV_FINAL = 9;

	/**
	 * Contrutor private porque esta e uma classe de utilitarios.
	 */
	private Mod11() {
		super();
	}

	/**
	 * <p>
	 * Calcula o somatorio dos algarismos do sequencial passado como parametro.
	 * </p>
	 *
	 * <p>
	 * Para obter o somatorio do sequencial do MOD-11 deve-se pegar o sequencial
	 * (Ex.: 15777), e em seguida atribuir pesos (9, 8, 7, 6, 5, 4, 3, 2) de
	 * forma inversa, ou seja: ((7*2) + (7*3) + (7*4) + (5*5) + (1*6) + (0*7) +
	 * (0*8) + (0*9)) == 94.<br/>
	 * </p>
	 *
	 * @param numero
	 *            String com o sequencial que se quer obter o somatorio.
	 * 
	 * @return O somatorio dos algarismos usando a logica necessaria para
	 *         calcular o MOD-11
	 */
	public static long sum(final String numero) {

		// Obtem o tamanho da inscricao.
		final int length = numero.length();

		// faz o somatorio de todos os elementos.
		long amount = 0L;

		// guarda o peso de referencia.
		byte base = BASE_INITIAL;

		// para cada algarismo, realiza o calculo.
		for (int i = 1; i < length + 1; i++) {

			// obtem a posicao que sera verificada.
			final int pos = length - i;

			// obtem o algarismo que sera somado.
			final byte algarism = Byte.valueOf(numero.substring(pos, pos + 1));

			// obtem a parcela para a soma.
			final int parcel = algarism * base;

			// realiza o somatorio.
			amount = amount + parcel;

			// incrementa o peso.
			base++;

			// valida o peso.
			if (base > Mod11.BASE_FINAL)
				base = BASE_INITIAL;
		}

		return amount;
	}

	/**
	 * <p>
	 * Retorna o digito verificador do sequencial indicado.
	 * </p>
	 * <p>
	 * Para obter o somatorio do sequencial MOD-11 (Ex.: 15777), atribuir pesos
	 * (9, 8, 7, 6, 5, 4, 3, 2) de forma inversa, ou seja: ((7*2) + (7*3) +
	 * (7*4) + (5*5) + (1*6) + (0*7) + (0*8) + (0*9)) == 94.<br/>
	 * </p>
	 *
	 * @param numero
	 *            somatorio do sequencial usado para a inscricao.
	 */
	public static byte getDV(final int numero) {

		// transforma para String.
		final String strSequential = String.valueOf(numero);

		return Mod11.getDV(strSequential);
	}

	/**
	 * <p>
	 * Baseado no sequencial informado calcula qual deveria ser o digito
	 * verificador.
	 * </p>
	 * <p>
	 * Para obter o somatorio do sequencial MOD-11 (Ex.: 15777), atribuir pesos
	 * (9, 8, 7, 6, 5, 4, 3, 2) de forma inversa, ou seja: ((7*2) + (7*3) +
	 * (7*4) + (5*5) + (1*6) + (0*7) + (0*8) + (0*9)) == 94.<br/>
	 * </p>
	 *
	 * @param numero
	 *            String com o sequencial que se quer obter o digito
	 *            verificador.
	 * @return o algarismo correspondente ao digito verificador.
	 */
	public static byte getDV(final String numero) {

		// obtem o somatorio do sequencial informado.
		final long sum = Mod11.sum(numero);

		// obtem o digito verificador calculado
		final byte mod = Mod11.calculaDigitoVerificador(sum);

		return mod;
	}

	/**
	 * Realiza o calculo do digito verificador baseado no somatorio do
	 * sequencial
	 *
	 * @param sum
	 *            Somatorio do sequencial MOD-11.
	 * @return o algarismo correspondente ao digito verificador.
	 */
	private static byte calculaDigitoVerificador(final long sum) {
		final byte MAX_ELEMENT = 10;

		Long result = 0L;

		// multiplica por 10 o somatorio.
		final long sequential10 = sum * 10;

		// pega o resto do calculo do mod-11
		if (sequential10 != 0)
			result = (sequential10 % 11);

		// resurso usado para evitar o problema do 'resta 1'
		if (result > DV_FINAL)
			result = result - MAX_ELEMENT;

		return result.byteValue();
	}

}
